From 3695303ba1539e57e5d8bdf4ee7ba1bc6e3edc81 Mon Sep 17 00:00:00 2001 From: Keir Fraser Date: Fri, 13 Nov 2009 22:00:19 +0000 Subject: [PATCH] xsm: Dynamic update to device ocontexts Added the ability to add and delete ocontexts dynamically on a running system. Two new commands have been added to the xsm hypercall, add and delete ocontext. Twelve new library functions have been implemented that use the hypercall commands to label and unlabel pirqs, PCI devices, I/O ports and memory. The base policy has been updated so dom0 has the ability to use the hypercall commands by default. Items added to the list will not be present next time the system reloads. They will need to be added to the static policy. Signed-off-by : George Coker Signed-off-by : Paul Nuzzi --- tools/flask/libflask/flask_op.c | 233 +++++++++++++++ tools/flask/libflask/include/flask.h | 19 ++ .../flask/policy/policy/flask/access_vectors | 2 + tools/flask/policy/policy/modules/xen/xen.te | 2 +- xen/include/public/xsm/flask_op.h | 4 +- xen/xsm/flask/flask_op.c | 103 ++++++- xen/xsm/flask/include/av_perm_to_string.h | 2 + xen/xsm/flask/include/av_permissions.h | 2 + xen/xsm/flask/include/security.h | 4 + xen/xsm/flask/ss/services.c | 274 ++++++++++++++++++ 10 files changed, 642 insertions(+), 3 deletions(-) diff --git a/tools/flask/libflask/flask_op.c b/tools/flask/libflask/flask_op.c index 579be20d96..9eab033e4d 100644 --- a/tools/flask/libflask/flask_op.c +++ b/tools/flask/libflask/flask_op.c @@ -109,3 +109,236 @@ int flask_setenforce(int xc_handle, int mode) return 0; } + +int flask_add_pirq(int xc_handle, unsigned int pirq, char *scontext) +{ + int err; + flask_op_t op; + char *buf; + char *pirq_s = OCON_PIRQ_STR; + int size = INITCONTEXTLEN + strlen(pirq_s) + (sizeof(unsigned int)) + + (sizeof(char) * 3); + + if ( (buf = (char *) malloc(size)) == NULL ) + return -ENOMEM; + memset(buf, 0, size); + + op.cmd = FLASK_ADD_OCONTEXT; + snprintf(buf, size, "%s %255s %u", pirq_s, scontext, pirq); + op.buf = buf; + op.size = size; + + if ( (err = xc_flask_op(xc_handle, &op)) != 0 ) + { + free(buf); + return err; + } + + free(buf); + return 0; + +} + +int flask_add_ioport(int xc_handle, unsigned long low, unsigned long high, + char *scontext) +{ + int err; + flask_op_t op; + char *buf; + char *ioport = OCON_IOPORT_STR; + int size = INITCONTEXTLEN + strlen(ioport) + + (sizeof(unsigned long) * 2) + (sizeof(char) * 4); + + if ( (buf = (char *) malloc(size)) == NULL ) + return -ENOMEM; + memset(buf, 0, size); + + op.cmd = FLASK_ADD_OCONTEXT; + snprintf(buf, size, "%s %255s %li %li", ioport, scontext, low, high); + op.buf = buf; + op.size = size; + + if ( (err = xc_flask_op(xc_handle, &op)) != 0 ) + { + free(buf); + return err; + } + + free(buf); + return 0; + +} + +int flask_add_iomem(int xc_handle, unsigned long low, unsigned long high, + char *scontext) +{ + int err; + flask_op_t op; + char *buf; + char *iomem = OCON_IOMEM_STR; + int size = INITCONTEXTLEN + strlen(iomem) + + (sizeof(unsigned long) * 2) + (sizeof(char) * 4); + + if ( (buf = (char *) malloc(size)) == NULL ) + return -ENOMEM; + memset(buf, 0, size); + + op.cmd = FLASK_ADD_OCONTEXT; + snprintf(buf, size, "%s %255s %li %li", iomem, scontext, low, high); + op.buf = buf; + op.size = size; + + if ( (err = xc_flask_op(xc_handle, &op)) != 0 ) + { + free(buf); + return err; + } + + free(buf); + return 0; + +} + +int flask_add_device(int xc_handle, unsigned long device, char *scontext) +{ + int err; + flask_op_t op; + char *buf; + char *dev = OCON_DEVICE_STR; + int size = INITCONTEXTLEN + strlen(dev) + (sizeof(unsigned long)) + + (sizeof(char) * 3); + + if ( (buf = (char *) malloc(size)) == NULL ) + return -ENOMEM; + memset(buf, 0, size); + + op.cmd = FLASK_ADD_OCONTEXT; + snprintf(buf, size, "%s %255s %li", dev, scontext, device); + op.buf = buf; + op.size = size; + + if ( (err = xc_flask_op(xc_handle, &op)) != 0 ) + { + free(buf); + return err; + } + + free(buf); + return 0; + +} + +int flask_del_pirq(int xc_handle, unsigned int pirq) +{ + int err; + flask_op_t op; + char *buf; + char *pirq_s = OCON_PIRQ_STR; + int size = strlen(pirq_s) + (sizeof(unsigned int)) + + (sizeof(char) * 2); + + if ( (buf = (char *) malloc(size)) == NULL ) + return -ENOMEM; + memset(buf, 0, size); + + op.cmd = FLASK_DEL_OCONTEXT; + snprintf(buf, size, "%s %u", pirq_s, pirq); + op.buf = buf; + op.size = size; + + if ( (err = xc_flask_op(xc_handle, &op)) != 0 ) + { + free(buf); + return err; + } + + free(buf); + return 0; + +} + +int flask_del_ioport(int xc_handle, unsigned long low, unsigned long high) +{ + int err; + flask_op_t op; + char *buf; + char *ioport = OCON_IOPORT_STR; + int size = strlen(ioport) + (sizeof(unsigned long) * 2) + + (sizeof(char) * 3); + + if ( (buf = (char *) malloc(size)) == NULL ) + return -ENOMEM; + memset(buf, 0, size); + + op.cmd = FLASK_DEL_OCONTEXT; + snprintf(buf, size, "%s %li %li", ioport, low, high); + op.buf = buf; + op.size = size; + + if ( (err = xc_flask_op(xc_handle, &op)) != 0 ) + { + free(buf); + return err; + } + + free(buf); + return 0; + +} + +int flask_del_iomem(int xc_handle, unsigned long low, unsigned long high) +{ + int err; + flask_op_t op; + char *buf; + char *iomem = OCON_IOMEM_STR; + int size = strlen(iomem) + (sizeof(unsigned long) * 2) + + (sizeof(char) * 3); + + if ( (buf = (char *) malloc(size)) == NULL ) + return -ENOMEM; + memset(buf, 0, size); + + op.cmd = FLASK_DEL_OCONTEXT; + snprintf(buf, size, "%s %li %li", iomem, low, high); + op.buf = buf; + op.size = size; + + if ( (err = xc_flask_op(xc_handle, &op)) != 0 ) + { + free(buf); + return err; + } + + free(buf); + return 0; + +} + +int flask_del_device(int xc_handle, unsigned long device) +{ + int err; + flask_op_t op; + char *buf; + char *dev = OCON_DEVICE_STR; + int size = strlen(dev) + (sizeof(unsigned long)) + (sizeof(char) * 2); + + if ( (buf = (char *) malloc(size)) == NULL ) + return -ENOMEM; + memset(buf, 0, size); + + op.cmd = FLASK_DEL_OCONTEXT; + snprintf(buf, size, "%s %li", dev, device); + op.buf = buf; + op.size = size; + + if ( (err = xc_flask_op(xc_handle, &op)) != 0 ) + { + free(buf); + return err; + } + + free(buf); + return 0; + +} diff --git a/tools/flask/libflask/include/flask.h b/tools/flask/libflask/include/flask.h index 31f6263404..44de26bb89 100644 --- a/tools/flask/libflask/include/flask.h +++ b/tools/flask/libflask/include/flask.h @@ -20,5 +20,24 @@ int flask_context_to_sid(int xc_handle, char *buf, uint32_t size, uint32_t *sid) int flask_sid_to_context(int xc_handle, int sid, char *buf, uint32_t size); int flask_getenforce(int xc_handle); int flask_setenforce(int xc_handle, int mode); +int flask_add_pirq(int xc_handle, unsigned int pirq, char *scontext); +int flask_add_ioport(int xc_handle, unsigned long low, unsigned long high, + char *scontext); +int flask_add_iomem(int xc_handle, unsigned long low, unsigned long high, + char *scontext); +int flask_add_device(int xc_handle, unsigned long device, char *scontext); +int flask_del_pirq(int xc_handle, unsigned int pirq); +int flask_del_ioport(int xc_handle, unsigned long low, unsigned long high); +int flask_del_iomem(int xc_handle, unsigned long low, unsigned long high); +int flask_del_device(int xc_handle, unsigned long device); +#define flask_add_single_ioport(x, l, s) flask_add_ioport(x, l, l, s) +#define flask_add_single_iomem(x, l, s) flask_add_iomem(x, l, l, s) +#define flask_del_single_ioport(x, l) flask_del_ioport(x, l, l) +#define flask_del_single_iomem(x, l) flask_del_iomem(x, l, l); +#define OCON_PIRQ_STR "pirq" +#define OCON_IOPORT_STR "ioport" +#define OCON_IOMEM_STR "iomem" +#define OCON_DEVICE_STR "pcidevice" +#define INITCONTEXTLEN 256 #endif /* __FLASK_H__ */ diff --git a/tools/flask/policy/policy/flask/access_vectors b/tools/flask/policy/policy/flask/access_vectors index 0df71d0a46..f835eb5a32 100644 --- a/tools/flask/policy/policy/flask/access_vectors +++ b/tools/flask/policy/policy/flask/access_vectors @@ -163,4 +163,6 @@ class security setenforce setbool setsecparam + add_ocontext + del_ocontext } diff --git a/tools/flask/policy/policy/modules/xen/xen.te b/tools/flask/policy/policy/modules/xen/xen.te index 851b0d6bd3..0977939146 100644 --- a/tools/flask/policy/policy/modules/xen/xen.te +++ b/tools/flask/policy/policy/modules/xen/xen.te @@ -51,7 +51,7 @@ allow dom0_t xen_t:xen firmware; allow dom0_t security_t:security {compute_av compute_create compute_member check_context load_policy compute_relabel compute_user setenforce setbool -setsecparam}; +setsecparam add_ocontext del_ocontext}; create_channel(dom0_t, dom0_t, evchn0-0_t) allow dom0_t evchn0-0_t:event {send}; diff --git a/xen/include/public/xsm/flask_op.h b/xen/include/public/xsm/flask_op.h index 7882d73cea..2fc1184f10 100644 --- a/xen/include/public/xsm/flask_op.h +++ b/xen/include/public/xsm/flask_op.h @@ -31,8 +31,10 @@ #define FLASK_AVC_HASHSTATS 18 #define FLASK_AVC_CACHESTATS 19 #define FLASK_MEMBER 20 +#define FLASK_ADD_OCONTEXT 21 +#define FLASK_DEL_OCONTEXT 22 -#define FLASK_LAST FLASK_MEMBER +#define FLASK_LAST FLASK_DEL_OCONTEXT typedef struct flask_op { uint32_t cmd; diff --git a/xen/xsm/flask/flask_op.c b/xen/xsm/flask/flask_op.c index 6d8faaf50c..f03b01106b 100644 --- a/xen/xsm/flask/flask_op.c +++ b/xen/xsm/flask/flask_op.c @@ -45,7 +45,9 @@ integer_param("flask_enabled", flask_enabled); 1UL<domain, SECURITY__DEL_OCONTEXT); + if ( len ) + return len; + + if ( (ocontext = xmalloc_bytes(size) ) == NULL ) + return -ENOMEM; + + len = sscanf(buf, "%s %li %li", ocontext, &low, &high); + if ( len < 2 ) + { + len = -EINVAL; + goto out; + } + else if ( len == 2 ) + high = low; + + if ( low > high ) + { + len = -EINVAL; + goto out; + } + + len = security_ocontext_del(ocontext, low, high); + out: + xfree(ocontext); + return len; +} + +static int flask_ocontext_add(char *buf, uint32_t size) +{ + int len = 0; + u32 sid = 0; + unsigned long low = 0; + unsigned long high = 0; + char *scontext; + char *ocontext; + + len = domain_has_security(current->domain, SECURITY__ADD_OCONTEXT); + if ( len ) + return len; + + if ( (scontext = xmalloc_bytes(size) ) == NULL ) + return -ENOMEM; + + if ( (ocontext = xmalloc_bytes(size) ) == NULL ) + { + xfree(scontext); + return -ENOMEM; + } + + memset(scontext, 0, size); + memset(ocontext, 0, size); + + len = sscanf(buf, "%s %s %li %li", ocontext, scontext, &low, &high); + if ( len < 3 ) + { + len = -EINVAL; + goto out; + } + else if ( len == 3 ) + high = low; + + if ( low > high ) + { + len = -EINVAL; + goto out; + } + len = security_context_to_sid(scontext, strlen(scontext)+1, &sid); + if ( len < 0 ) + { + len = -EINVAL; + goto out; + } + len = security_ocontext_add(ocontext, low, high, sid); +out: + xfree(ocontext); + xfree(scontext); + return len; +} + long do_flask_op(XEN_GUEST_HANDLE(xsm_op_t) u_flask_op) { flask_op_t curop, *op = &curop; @@ -910,6 +999,18 @@ long do_flask_op(XEN_GUEST_HANDLE(xsm_op_t) u_flask_op) } break; + case FLASK_ADD_OCONTEXT: + { + length = flask_ocontext_add(arg, op->size); + break; + } + + case FLASK_DEL_OCONTEXT: + { + length = flask_ocontext_del(arg, op->size); + break; + } + default: length = -ENOSYS; break; diff --git a/xen/xsm/flask/include/av_perm_to_string.h b/xen/xsm/flask/include/av_perm_to_string.h index 125aa97a3e..da56d563c9 100644 --- a/xen/xsm/flask/include/av_perm_to_string.h +++ b/xen/xsm/flask/include/av_perm_to_string.h @@ -110,3 +110,5 @@ S_(SECCLASS_SECURITY, SECURITY__SETENFORCE, "setenforce") S_(SECCLASS_SECURITY, SECURITY__SETBOOL, "setbool") S_(SECCLASS_SECURITY, SECURITY__SETSECPARAM, "setsecparam") + S_(SECCLASS_SECURITY, SECURITY__ADD_OCONTEXT, "add_ocontext") + S_(SECCLASS_SECURITY, SECURITY__DEL_OCONTEXT, "del_ocontext") diff --git a/xen/xsm/flask/include/av_permissions.h b/xen/xsm/flask/include/av_permissions.h index 23b0117f3e..cc1f0a2ccb 100644 --- a/xen/xsm/flask/include/av_permissions.h +++ b/xen/xsm/flask/include/av_permissions.h @@ -118,4 +118,6 @@ #define SECURITY__SETENFORCE 0x00000080UL #define SECURITY__SETBOOL 0x00000100UL #define SECURITY__SETSECPARAM 0x00000200UL +#define SECURITY__ADD_OCONTEXT 0x00000400UL +#define SECURITY__DEL_OCONTEXT 0x00000800UL diff --git a/xen/xsm/flask/include/security.h b/xen/xsm/flask/include/security.h index 149caf753c..8a64b0a3e5 100644 --- a/xen/xsm/flask/include/security.h +++ b/xen/xsm/flask/include/security.h @@ -82,4 +82,8 @@ int security_device_sid(u32 device, u32 *out_sid); int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid, u16 tclass); +int security_ocontext_add(char *ocontext, unsigned long low, + unsigned long high, u32 sid); + +int security_ocontext_del(char *ocontext, unsigned int low, unsigned int high); #endif /* _FLASK_SECURITY_H_ */ diff --git a/xen/xsm/flask/ss/services.c b/xen/xsm/flask/ss/services.c index f841aa710e..6e728009e4 100644 --- a/xen/xsm/flask/ss/services.c +++ b/xen/xsm/flask/ss/services.c @@ -1942,3 +1942,277 @@ out: xfree(bvalues); return rc; } + +int determine_ocontext( char *ocontext ) +{ + if ( strcmp(ocontext, "pirq") == 0 ) + return OCON_PIRQ; + else if ( strcmp(ocontext, "ioport") == 0 ) + return OCON_IOPORT; + else if ( strcmp(ocontext, "iomem") == 0 ) + return OCON_IOMEM; + else if ( strcmp(ocontext, "pcidevice") == 0 ) + return OCON_DEVICE; + else + return -1; +} + +int security_ocontext_add( char *ocontext, unsigned long low, unsigned long high + ,u32 sid ) +{ + int ret = 0; + int ocon = 0; + struct ocontext *c; + struct ocontext *add; + + if ( (ocon = determine_ocontext(ocontext)) < 0 ) + return -EINVAL; + if ( (add = xmalloc(struct ocontext)) == NULL ) + return -ENOMEM; + memset(add, 0, sizeof(struct ocontext)); + add->sid[0] = sid; + + POLICY_WRLOCK; + switch( ocon ) + { + case OCON_PIRQ: + add->u.pirq = (u16)low; + if ( high != low ) + { + ret = -EINVAL; + break; + } + + c = policydb.ocontexts[OCON_PIRQ]; + while ( c ) + { + if ( c->u.pirq == add->u.pirq ) + { + printk("%s: Duplicate pirq %d\n", __FUNCTION__, add->u.pirq); + ret = -EINVAL; + break; + } + c = c->next; + } + + if ( ret == 0 ) + { + add->next = policydb.ocontexts[OCON_PIRQ]; + policydb.ocontexts[OCON_PIRQ] = add; + } + break; + + case OCON_IOPORT: + add->u.ioport.low_ioport = low; + add->u.ioport.high_ioport = high; + + c = policydb.ocontexts[OCON_IOPORT]; + while ( c ) + { + if ( c->u.ioport.low_ioport <= add->u.ioport.high_ioport && + add->u.ioport.low_ioport <= c->u.ioport.high_ioport ) + { + printk("%s: IO Port overlap with entry 0x%x - 0x%x\n", + __FUNCTION__, c->u.ioport.low_ioport, + c->u.ioport.high_ioport); + ret = -EINVAL; + break; + } + c = c->next; + } + + if ( ret == 0 ) + { + add->next = policydb.ocontexts[OCON_IOPORT]; + policydb.ocontexts[OCON_IOPORT] = add; + } + break; + + case OCON_IOMEM: + add->u.iomem.low_iomem = low; + add->u.iomem.high_iomem = high; + + c = policydb.ocontexts[OCON_IOMEM]; + while ( c ) + { + if ( c->u.iomem.low_iomem <= add->u.iomem.high_iomem && + add->u.iomem.low_iomem <= c->u.iomem.high_iomem ) + { + printk("%s: IO Memory overlap with entry 0x%x - 0x%x\n", + __FUNCTION__, c->u.iomem.low_iomem, + c->u.iomem.high_iomem); + ret = -EINVAL; + break; + } + c = c->next; + } + + if ( ret == 0 ) + { + add->next = policydb.ocontexts[OCON_IOMEM]; + policydb.ocontexts[OCON_IOMEM] = add; + } + break; + + case OCON_DEVICE: + add->u.device = low; + if ( high != low ) + { + ret = -EINVAL; + break; + } + + c = policydb.ocontexts[OCON_DEVICE]; + while ( c ) + { + if ( c->u.device == add->u.device ) + { + printk("%s: Duplicate PCI Device 0x%x\n", __FUNCTION__, + add->u.device); + ret = -EINVAL; + break; + } + c = c->next; + } + + if ( ret == 0 ) + { + add->next = policydb.ocontexts[OCON_DEVICE]; + policydb.ocontexts[OCON_DEVICE] = add; + } + break; + + default: + ret = -EINVAL; + } + POLICY_WRUNLOCK; + + if ( ret != 0 ) + xfree(add); + return ret; +} + +int security_ocontext_del( char *ocontext, unsigned int low, unsigned int high ) +{ + int ret = 0; + int ocon = 0; + struct ocontext *c, *before_c; + + if ( (ocon = determine_ocontext(ocontext)) < 0 ) + return -EINVAL; + + POLICY_WRLOCK; + switch( ocon ) + { + case OCON_PIRQ: + for ( before_c = NULL, c = policydb.ocontexts[OCON_PIRQ]; + c; before_c = c, c = c->next ) + { + if ( c->u.pirq == low ) + { + if ( before_c == NULL ) + { + policydb.ocontexts[OCON_PIRQ] = c->next; + xfree(c); + goto out; + } + else + { + before_c->next = c->next; + xfree(c); + goto out; + } + } + } + + printk("%s: ocontext not found: pirq %d\n", __FUNCTION__, low); + ret = -EINVAL; + break; + + case OCON_IOPORT: + for ( before_c = NULL, c = policydb.ocontexts[OCON_IOPORT]; + c; before_c = c, c = c->next ) + { + if ( c->u.ioport.low_ioport == low && + c->u.ioport.high_ioport == high ) + { + if ( before_c == NULL ) + { + policydb.ocontexts[OCON_IOPORT] = c->next; + xfree(c); + goto out; + } + else + { + before_c->next = c->next; + xfree(c); + goto out; + } + } + } + + printk("%s: ocontext not found: ioport 0x%x - 0x%x\n", __FUNCTION__, + low, high); + ret = -EINVAL; + break; + + case OCON_IOMEM: + for ( before_c = NULL, c = policydb.ocontexts[OCON_IOMEM]; + c; before_c = c, c = c->next ) + { + if ( c->u.iomem.low_iomem == low && + c->u.iomem.high_iomem == high ) + { + if ( before_c == NULL ) + { + policydb.ocontexts[OCON_IOMEM] = c->next; + xfree(c); + goto out; + } + else + { + before_c->next = c->next; + xfree(c); + goto out; + } + } + } + + printk("%s: ocontext not found: iomem 0x%x - 0x%x\n", __FUNCTION__, + low, high); + ret = -EINVAL; + break; + + case OCON_DEVICE: + for ( before_c = NULL, c = policydb.ocontexts[OCON_DEVICE]; + c; before_c = c, c = c->next ) + { + if ( c->u.device == low ) + { + if ( before_c == NULL ) + { + policydb.ocontexts[OCON_DEVICE] = c->next; + xfree(c); + goto out; + } + else + { + before_c->next = c->next; + xfree(c); + goto out; + } + } + } + + printk("%s: ocontext not found: pcidevice 0x%x\n", __FUNCTION__, low); + ret = -EINVAL; + break; + + default: + ret = -EINVAL; + } + + out: + POLICY_WRUNLOCK; + return ret; +} -- 2.30.2